import * as THREE from "three";

import { GetURLContent } from "./getContentFromUrl";
import { ParseLakeDATFromFileContent,parsePltFileGetDataArray,parsePltFile2Threejs,
    Parse5LevelLakeDATFromFileContent,Parse5LevelLakeWQRDATFromFileContent,
    CreateGeometryFromPLT,CreateFiveLevelGeometryFromPLTAndDAT,
    CreateFiveLevelGeometryFromPLTAndFiveLevelDAT,CreateFiveLevelGeometryFromPLTAndFiveLevelDAT2,
    getHydroDataArrayMinMax,
} from "./functionsLab";//核心函数库

import { Value2Color } from "./dealPropertyValue";
import { Point } from './hydroData'

import { HydrodynamicData, HydrodynamicDataWithIndex } from "./hydroData";
import { CreateGeometryWithColor, CreateSphereMesh, createCubeMesh, } from "./createModel";

export async function Main(scene:THREE.Scene){
    console.log("Main Function....");

    // TODO：写死的内容
    const PointCount = 4578;
    // X Y ZB 4578
    const EdgeCount = 2124;
    // DEP Q T U V W Z
    // Index : 2124 * 8 = 16992

    // 读取PLT文件获取网格模型索引顺序情况   
    const pltFileURL = "http://127.0.0.1:8080/TEC_HYD/TEC_HYD_180.000.PLT";//将二维honghu/res/文件夹发布出来
    const fileContent = await GetURLContent(pltFileURL);

    let lakeBlockData = parsePltFileGetDataArray(fileContent);
    console.log("blockData",lakeBlockData);

    // // 构建Geometry
    // let lakeGeometry = parsePltFile2Threejs(fileContent);//需注意返回的是geometry还是mesh
    // 创建材质和网格
    const material = new THREE.MeshBasicMaterial({
        // color: 0xffffff,
        vertexColors: true, // 启用顶点颜色
        wireframe: true,   // 根据需求选择是否启用线框模式
        // side: THREE.DoubleSide,
        transparent: true,
    });

    // const lakeMesh = new THREE.Mesh(lakeGeometry, material);
    // scene.add(lakeMesh);

    
    // 读取单层DAT文件获取网格材质
    const datFileURL = "http://127.0.0.1:8080/TIME_HYD/HYD_2024_10_1_0_0_0.DAT";//一样也是二维honghu/res/文件夹下的内容
    
    const datFileContent = await GetURLContent(datFileURL);
    let hydroDataArray = ParseLakeDATFromFileContent(datFileContent);
    // console.log("DAT输入点信息：",hydroDataArray);

    // // 绘制并查看DAT数据中记录的各点的位置情况
    // const originPoint = new Point(432940.90625, 3288375, 22);// PLT文件中的记录的相对坐标原点的位置
    // hydroDataArray.forEach(hydroData => {
    //     let sphereMesh = CreateSphereMesh(hydroData.X-originPoint.x,hydroData.Y-originPoint.y,hydroData.Z-originPoint.z);
    //     scene.add(sphereMesh);
    // });

    // TODO:展示水体表层所记录的waterLevel和waterDepth的变化
    
    console.log("Temperater range:");
    let minmaxTemper = getHydroDataArrayMinMax(hydroDataArray,"temperature");

    let minmaxWaterLevel = getHydroDataArrayMinMax(hydroDataArray, "waterLevel");
    let minmaxWaterDepth = getHydroDataArrayMinMax(hydroDataArray,"waterDepth");


    // 测试用:读取DAT文件中顶点记录的值，转为顶点颜色进行展示
    // let newMesh = dealPLTGeometryWithDAT(hydroDataArray, lakeGeometry);
    // scene.add(newMesh);

    // NOTE:一个效果还可以的结果
    let fiveLevelGeometry = CreateFiveLevelGeometryFromPLTAndDAT(lakeBlockData,hydroDataArray);
    const lakeMesh = new THREE.Mesh(fiveLevelGeometry, material);
    scene.add(lakeMesh);


    // NOTE:用于判断DAT数据采用的是PLT文件中哪些顶点
    // DAT中记录的点与PLT顶点索引为顺序索引，2290-4413
    // PointDataMatch(hydroDataArray,lakeBlockData);




    // 组合DAT用于材质、PLT用于建模

    //  TODO：读取DAT数据后针对PLT建模结果进行拉伸
    // let mesh = geometryVertexUseDATData(hydroDataArray, lakeBlockData);
    // scene.add(mesh);








   

    // TODO：
    // 读取五层DAT文件获取网格材质
    // const fiveLevelDatFileURL = "http://192.168.48.1:8082/HYD_2023_12_1_0_0_0.DAT";
    // const fiveLevelDatFileContent = await GetURLContent(fiveLevelDatFileURL);
    // let fiveLevelHydroDataArray =  Parse5LevelLakeDATFromFileContent(fiveLevelDatFileContent);

    // // console.log("DAT输入点个数为：",fiveLevelHydroDataArray.length);
    // console.log("DAT输入点为：",fiveLevelHydroDataArray);



    // // TODO:读取5层湖泊模型的PLT文件--暂时舍弃
    // const pltFileURL = "http://192.168.48.1:8083/TEC_HYD/TEC_HYD_0.000.PLT";
    // const fileContent = await GetURLContent(pltFileURL);

    // let fiveLevelGeometry = parseFiveLevelPLTFileConten(fileContent);
    // scene.add(fiveLevelGeometry);



}

export async function LoadModelMain(scene:THREE.Scene){
    console.log("LoadModelMain Function....");

    // 构建洪湖三维模型

    const PointCount = 4578;
    // X Y ZB 4578
    const EdgeCount = 2124;
    // DEP Q T U V W Z
    // Index : 2124 * 8 = 16992

    // 读取二维PLT文件获取网格模型索引顺序情况   
    const pltFileURL = "http://127.0.0.1:8080/TEC_HYD/TEC_HYD_180.000.PLT";//将二维honghu/res/文件夹发布出来
    const fileContent = await GetURLContent(pltFileURL);

    let lakeBlockData = parsePltFileGetDataArray(fileContent);
    console.log("blockData",lakeBlockData);

    // 创建材质和网格
    const material = new THREE.MeshBasicMaterial({
        // color: 0xffffff,
        vertexColors: true, // 启用顶点颜色
        // wireframe: true,   // 根据需求选择是否启用线框模式
        // side: THREE.DoubleSide,
        transparent: true,
    });

    //TODO:读取三维网格数据 5层DAT文件，并处理每层顶点
    //将 洪湖三维模型river_2d/river_2d/res 文件夹发布出来
    // const fiveLevelDATFileURL = "http://127.0.0.1:8081/TIME_HYD/HYD_2023_12_2_10_0_40.DAT";

    // TODO：将洪湖的WQR模拟结果构建模型，以NH4进行可视化
    const fiveLevelDATFileURL = "http://127.0.0.1:8081/TIME_WQR/WQR_2023_12_2_10_0_40.DAT";


    const datFileContent = await GetURLContent(fiveLevelDATFileURL);

    let fiveLevelWQRDataArray = Parse5LevelLakeWQRDATFromFileContent(datFileContent);
    console.log("LevelHydroDataArray length:",fiveLevelWQRDataArray[0].length);
    // console.log("Dat data content:", fiveLevelWQRDataArray[0][0].toString());
    console.log("fiveLevelWQRDataArray length:",fiveLevelWQRDataArray.length);
    
    // TODO:模拟结果
    console.log("NH4 range:");
    let minmaxNHX = getHydroDataArrayMinMax(fiveLevelWQRDataArray[0],"NHX");


    let fiveLevelGeometry = CreateFiveLevelGeometryFromPLTAndFiveLevelDAT2(lakeBlockData, fiveLevelWQRDataArray,"NHX",0,1);
    console.log("fiveLevelGeometry:", fiveLevelGeometry);

    // NOTE:一个效果还可以的结果
    const lakeMesh = new THREE.Mesh(fiveLevelGeometry, material);
    scene.add(lakeMesh);

}

// 从PLT文件中寻找DAT每个点对应的点的索引（从0开始）
function PointDataMatch(hydroDataArray:Array<HydrodynamicDataWithIndex>,lakeBlockData:{ [key: string]: number[] }){
    const pltPointCount = lakeBlockData.X.length;// pointCount

    let pltPoints = [];
    for(let i=0;i<pltPointCount;i++){
        let tmpPoint = new Point(lakeBlockData.X[i],lakeBlockData.Y[i],lakeBlockData.Z[i]);
        pltPoints.push(tmpPoint);
    }

    // let hydroPoint = [];// edges * 3 = 6372
    let datPoints = [];
    hydroDataArray.forEach(hydroData => {
        // hydroPoint.push(hydroData.X,hydroData.Y,hydroData.Z);

        let tmpPoint = new Point(hydroData.X, hydroData.Y, hydroData.Z);
        datPoints.push(tmpPoint);
    });

    let count = countPointsInBothArrays(pltPoints,datPoints);
    console.log("match number:", count);


function countPointsInBothArrays(A: Array<Point>, B: Array<Point>): number {
    // Create a Map from array A for efficient lookups
    const mapA = new Map<string, number>();
    let index = 0;

    A.forEach(point => {
        // Convert point coordinates to a string key
        // const key = `${point.x},${point.y},${point.z}`;
        const key = `${point.x},${point.y}`;
        mapA.set(key, index);//手动给每个点插入索引，从0开始
        index++;
    });
    
    // Count how many points in B are also in A
    let count = 0;

    let matchIndexArray = [];
    B.forEach(point => {
        // const key = `${point.x},${point.y},${point.z}`;
        const key = `${point.x},${point.y}`;

        if (mapA.has(key)) {
            count++;
            // console.log("Match point index:",mapA.get(key));
            matchIndexArray.push(mapA.get(key));
        }
    });
    
    let indexArrayFlag = isConsecutiveIncreasing(matchIndexArray)
    if(indexArrayFlag){
        console.log("indexArray is ordered.And last substract before is one.")
    }else{
        console.log("indexArray is not ordered.")
    
    }

    return count;

    function isConsecutiveIncreasing(arr) {
        // 首先检查数组是否为空或只有一个元素，这样的数组也视为满足条件
        if (arr.length <= 1) return true;
    
        // 然后遍历数组，从第二个元素开始检查
        for (let i = 1; i < arr.length; i++) {
            // 如果当前元素不等于前一个元素加1，则返回false
            if (arr[i] !== arr[i - 1] + 1) {
                return false;
            }
        }
    
        // 如果循环结束都没有返回false，则说明数组是自增的，返回true
        return true;
    }

}

}




// FIXME:顶点坐标改变，效果不佳
function geometryVertexUseDATData(hydroDataArray: Array<HydrodynamicDataWithIndex>, blockData:{ [key: string]: number[] }){

const pointCount = blockData.X.length;
console.log("参与建模点的个数为：",pointCount);

// INFO测试1，重新建模。总结：不可取
// 获取PLT文件中用于建模的顶点,全部忽略z值
let vertexArray = [];

const originPoint = [blockData.X[0], blockData.Y[0],0]
console.log("originPoint:", originPoint);

// 顶点数组
for (let i = 0; i < blockData.X.length; i++) {
    vertexArray.push(blockData.X[i]-originPoint[0], blockData.Y[i]-originPoint[1], 0);
}
// console.log("befor vertexArray has z value:", vertexArray);

// 修改第2291-4414个点的z值
let startIndex = 2290;

// 考虑添加顶点颜色...
const colors = new Float32Array(pointCount * 3);

let valueData = [];
hydroDataArray.forEach(hydroData => {
    valueData.push(hydroData.waterLevel);
});
const minValue = Math.min(...valueData);
const maxValue = Math.max(...valueData);
console.log(`valueData data, min:${minValue} ,max:${maxValue}`);

// INFO:如果只修改出现的点的位置，会出现缝隙
for(let i=0;i<hydroDataArray.length;i++){
    let tmpIndex = (startIndex + i) * 3 ;
    const scale = 1000;
    vertexArray[tmpIndex + 2] = hydroDataArray[i].waterLevel+hydroDataArray[i].waterDepth * scale;// z取值水位+水深

    // INFO:测试用于展示颜色的变量，顺手而为。效果一般
    const value = hydroDataArray[i].waterLevel;
    // 将 value 映射到颜色
    const color = Value2Color(value, 0, 30); // 假设 temperature 范围为 [0, 50]
    const normalizedColor = [
        color[0] / 255.0, // R
        color[1] / 255.0, // G
        color[2] / 255.0, // B
    ];
    // 设置颜色
    colors[tmpIndex] = normalizedColor[0];     // R
    colors[tmpIndex + 1] = normalizedColor[1]; // G
    colors[tmpIndex + 2] = normalizedColor[2]; // B

}
// console.log("after vertexArray has z value:", vertexArray);

// INFO:处理剩余顶点，颜色设置为白色。最终的效果便是只有修改值的地方为其他颜色
for (let i = 0; i < vertexArray.length / 3; i++) {
    if (colors[i * 3] === 0 && colors[i * 3 + 1] === 0 && colors[i * 3 + 2] === 0) {
        colors[i * 3] = 1.0; // R
        colors[i * 3 + 1] = 1.0; // G
        colors[i * 3 + 2] = 1.0; // B
    }
}


// 构建索引数组
let indexArray = [];
for (let i = 0; i < blockData.index.length / 8; i++) {
    // i为index每一行的内容
    indexArray.push(
        blockData.index[i*8+1]-1, blockData.index[i*8+3]-1, blockData.index[i*8+2]-1,//face1-正面
        blockData.index[i*8+1]-1, blockData.index[i*8+4]-1, blockData.index[i*8+3]-1,//face2-坐标轴正方向上正面可视
    );
}

// 构建Geometry
let geometry =  CreateGeometryWithColor(new Float32Array(vertexArray), new Uint16Array(indexArray),colors);


// 创建材质和网格
const material = new THREE.MeshBasicMaterial({
    // color: 0xffffff,
    vertexColors: true, // 启用顶点颜色
    wireframe: true,   // 根据需求选择是否启用线框模式
    // side: THREE.DoubleSide,
});

const mesh = new THREE.Mesh(geometry, material);

return mesh;

// // INFO:测试2，修改geomtery顶点的情况，一样出现尖刺情况。但颜色是均匀的
// let geometry = CreateGeometryFromPLT(blockData);

// // 获取几何体的顶点位置和索引
// const positions = geometry.attributes.position.array; // 顶点位置
// const indexArray = geometry.index.array; // 索引数组

// // console.log("geometry.position:", positions); // 顶点坐标数组长度
// // console.log("geometry.index:", indexArray); // 索引数组长度

// // 初始化颜色数组（顶点数量 * 3）
// const colors = new Float32Array(positions.length);

// // 遍历 hydroDataArray
// hydroDataArray.forEach((data, dataIndex) => {
//     //测试用于展示颜色的变量
//     const value = data.temperature;
//     // 将 value 映射到颜色
//     const color = Value2Color(value, 0, 50);
//     const normalizedColor = [
//         color[0] / 255.0, // R
//         color[1] / 255.0, // G
//         color[2] / 255.0, // B
//     ];

//     // const cx = data.X; // 中心点 X 坐标
//     // const cy = data.Y; // 中心点 Y 坐标
//     const cz = data.waterDepth; // 中心点 Z 坐标

//     // 获取当前数据对应的索引范围
//     const startIndex = dataIndex * 6;
//     const endIndex = startIndex + 6;

//     // 遍历索引范围
//     for (let i = startIndex; i < endIndex; i++) {
//         const vertexIndex = indexArray[i]; // 获取索引指向的顶点索引

//         // NOTE:夸大z值情况，查看顶点拉伸效果
//         const scale = 1;
//         // 拉伸顶点的 Z 值
//         positions[vertexIndex * 3 + 2] += cz * scale; // 更新顶点 Z 值为 dat 数据中的 Z

//         // 设置颜色
//         colors[vertexIndex * 3] = normalizedColor[0];     // R
//         colors[vertexIndex * 3 + 1] = normalizedColor[1]; // G
//         colors[vertexIndex * 3 + 2] = normalizedColor[2]; // B
//     }
// });

// // NOTE:更新几何体的颜色属性，会进行插值
// geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
// geometry.attributes.color.needsUpdate = true;

// // INFO:顶点不会进行插值
// geometry.attributes.position.needsUpdate = true;

// // 创建材质和网格
// const material = new THREE.MeshBasicMaterial({
//     vertexColors: true, // 启用顶点颜色
//     // wireframe: true,   // 根据需求选择是否启用线框模式
//     // side: THREE.DoubleSide,
// });

// const mesh = new THREE.Mesh(geometry, material);

// return mesh;


}



export async function CreateModelFromData(){
    // 读取PLT文件获取网格模型索引顺序情况，利用二维模型数据PLT进行建模
    const pltFileURL = "http://127.0.0.1:8080/TEC_HYD/TEC_HYD_180.000.PLT";//将 /模型模拟结果文件/honghu/res 文件夹发布出来
    // const pltFileURL = "http://127.0.0.1:8080/TEC_WQR/TEC_WQRSUM_180.000.PLT";

    //TODO:读取5层DAT文件，并处理每层顶点
    // const fiveLevelDATFileURL = "http://127.0.0.1:8081/TIME_HYD/HYD_2023_12_2_10_0_40.DAT";//将 洪湖三维模型river_2d/river_2d/res 文件夹发布出来
    
    // 将洪湖的WQR模拟结果构建模型，以NH4进行可视化
    const fiveLevelDATFileURL = "http://localhost:8081/TIME_WQR/WQR_2023_12_2_10_0_40.DAT";



    let fiveLevelGeometry = CreateGeometryFromURL(pltFileURL,fiveLevelDATFileURL);

    return fiveLevelGeometry;
}

export async function CreateGeometryFromURL(pltFileURL:string,fiveLevelDATFileURL:string){
    const fileContent = await GetURLContent(pltFileURL);
    
    let lakeBlockData = parsePltFileGetDataArray(fileContent);
    
    const datFileContent = await GetURLContent(fiveLevelDATFileURL);
    
    let fiveLevelHydroDataArray = Parse5LevelLakeWQRDATFromFileContent(datFileContent);
    // console.log("fiveLevelHydroDataArray length:",fiveLevelHydroDataArray.length);
    
    // TODO:模拟结果依旧存在问题
    // let fiveLevelGeometry = CreateFiveLevelGeometryFromPLTAndFiveLevelDAT2(lakeBlockData, fiveLevelHydroDataArray,"Z",10,25);

    // console.log("NH4 range:");
    // let minmaxNHX = getHydroDataArrayMinMax(fiveLevelHydroDataArray[0],"NHX");

    let fiveLevelGeometry = CreateFiveLevelGeometryFromPLTAndFiveLevelDAT2(lakeBlockData, fiveLevelHydroDataArray,"NHX",0.1,0.9);
    
    return fiveLevelGeometry;
}